package io.github.annyylxyms.gyn.gateway.plugin;

import cn.gyn.gynrpc.core.api.LoadBalancer;
import cn.gyn.gynrpc.core.api.RegistryCenter;
import cn.gyn.gynrpc.core.cluster.RoundRibbonLoadBalancer;
import cn.gyn.gynrpc.core.meta.InstanceMeta;
import cn.gyn.gynrpc.core.meta.ServiceMeta;
import io.github.annyylxyms.gyn.gateway.AbstractGatewayPlugin;
import io.github.annyylxyms.gyn.gateway.GatewayPluginChain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * gyn rpc gateway plugin
 * @date 2024-12-17
 */
@Component("gynrpc")
public class GynRpcPlugin extends AbstractGatewayPlugin {
    public static final String NAME = "gynrpc";
    private String prefix = GATEWAY_PREFIX + "/" + NAME + "/";
    @Autowired
    RegistryCenter rc;
    LoadBalancer<InstanceMeta> loadBalancer = new RoundRibbonLoadBalancer<>();

    @Override
    public Mono<Void> doHandle(ServerWebExchange exchange, GatewayPluginChain chain) {
        System.out.println("===>>>>> [GynRpcPlugin] ...");
        //1、通过请求拿到路径里service的全限定名,substring从第四个字符取到结束"/gw/**" ，现在改成前缀长度
        String service = exchange.getRequest().getPath().value().substring(prefix.length());
        //从注册中心拿到服务实例的列表：两种办法：1、可以照搬注册中心代码过来，2、也可以打包注册中心的jar包引入进来

        //2、通过rc拿到所有活着的服务实例（这里可以优化加fetchAll缓存、加动态订阅、增量订阅）
        ServiceMeta serviceMeta = ServiceMeta.builder().name(service)
                .app("app1").env("dev").namespace("public").build();
        List<InstanceMeta> instanceMetas = rc.fetchAll(serviceMeta);

        //3、先简化处理，获取第一个实例url
        //String url = instanceMetas.get(0).toUrl();
        //换成LB看看网关是不是确实取到了多个
        InstanceMeta instanceMeta = loadBalancer.choose(instanceMetas);
        System.out.println("instanceMetas size = " + instanceMetas.size() + " ,loadBalancer choose:" + instanceMeta);
        String url = instanceMeta.toUrl();

        //4、拿到请求的报文封装报文请求服务
        Flux<DataBuffer> requestBody = exchange.getRequest().getBody();

        //5、通过webclient发送POST请求
        WebClient client = WebClient.create(url);
        Mono<ResponseEntity<String>> entity = client.post()
                .header("Content-Type", "application/json")
                .body(requestBody, DataBuffer.class).retrieve().toEntity(String.class);

        //6、通过entity获取响应报文
        Mono<String> body = entity.map(ResponseEntity::getBody);
        //body.subscribe(source -> System.out.println("response:" + source)); //这行加了为啥报错呢？

        //7、组装响应报文（不能链式操作，一个个加）
        exchange.getResponse().getHeaders().add("Content-Type", "application/json");
        exchange.getResponse().getHeaders().add("gyn.gw.version", "v1.0.0");
        exchange.getResponse().getHeaders().add("gyn.gw.plugin", getName());

        //flatMap先不用了解，把它当成java8里的stream处理即可 x相当于拿到body里封装的数据了
        return body.flatMap(x -> exchange.getResponse()
                        .writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(x.getBytes()))))
                .then(chain.handle(exchange));
    }

    /**支持走dohandle ,不支持就不走*/
    @Override
    public boolean doSupport(ServerWebExchange exchange) {
        return exchange.getRequest().getPath().value().startsWith(prefix);
    }

    @Override
    public String getName() {
        return NAME;
    }
}
